package io.sundial.coordination;

import com.google.common.base.Preconditions;
import io.sundial.coordination.node.Node;
import io.sundial.coordination.node.NodeWatcher;
import io.sundial.coordination.tree.TreeWatcher;
import io.sundial.coordination.vote.VoteWatcher;
import io.sundial.coordination.watching.EventWatching;
import io.sundial.core.Callback;
import io.sundial.core.lifecycle.Lifecycle;

/**
 * 集群协调者
 *
 * @author Payne 646742615@qq.com
 * 2018/12/21 10:10
 */
public interface Coordinator extends Lifecycle {

    /**
     * 同步节点创建
     *
     * @param path       节点路径
     * @param data       节点数据
     * @param durable    是否持久化
     * @param sequential 是否顺序化
     * @throws CoordinatorException 协调者创建异常
     */
    String create(
            String path,
            byte[] data,
            boolean durable,
            boolean sequential
    ) throws CoordinatorException;

    /**
     * 异步节点创建
     *
     * @param path       节点路径
     * @param data       节点数据
     * @param durable    是否持久化
     * @param sequential 是否顺序化
     * @param callback   创建完成回调
     * @throws CoordinatorException 协调者创建异常
     */
    void create(
            String path,
            byte[] data,
            boolean durable,
            boolean sequential,
            Callback<CreateResult, CallbackException> callback
    ) throws CoordinatorException;

    /**
     * 异步带上下文的节点创建
     *
     * @param path       节点路径
     * @param data       节点数据
     * @param durable    是否持久化
     * @param sequential 是否顺序化
     * @param callback   创建完成回调
     * @param context    上下文对象
     * @throws CoordinatorException 协调者创建异常
     */
    void create(
            String path,
            byte[] data,
            boolean durable,
            boolean sequential,
            Callback<CreateResult, CallbackException> callback,
            Object context
    ) throws CoordinatorException;

    Node obtain(String path) throws CoordinatorException;

    void obtain(String path, Callback<ObtainResult, CallbackException> callback) throws CoordinatorException;

    void obtain(String path, Callback<ObtainResult, CallbackException> callback, Object context) throws CoordinatorException;

    Node update(String path, byte[] data, int version) throws CoordinatorException;

    void update(String path, byte[] data, int version, Callback<UpdateResult, CallbackException> callback) throws CoordinatorException;

    void update(String path, byte[] data, int version, Callback<UpdateResult, CallbackException> callback, Object context) throws CoordinatorException;

    void delete(String path, int version, boolean guaranteed, boolean recursived) throws CoordinatorException;

    void delete(String path, int version, boolean guaranteed, boolean recursived, Callback<DeleteResult, CallbackException> callback) throws CoordinatorException;

    void delete(String path, int version, boolean guaranteed, boolean recursived, Callback<DeleteResult, CallbackException> callback, Object context) throws CoordinatorException;

    /**
     * 参选
     *
     * @param path    参选节点路径
     * @param watcher 选举监听器
     */
    EventWatching elect(
            String path,
            VoteWatcher watcher
    ) throws CoordinatorException;

    EventWatching elect(
            String path,
            VoteWatcher watcher,
            boolean now
    ) throws CoordinatorException;

    /**
     * 监听给定的单个节点
     * 只监听该节点的变化，不监听子节点变化。
     *
     * @param path    节点路径
     * @param watcher 监听器
     * @throws CoordinatorException 协调者创建异常
     */
    EventWatching watch(
            String path,
            NodeWatcher watcher
    ) throws CoordinatorException;

    /**
     * 监听给定的单个节点
     * 只监听该节点的变化，不监听子节点变化。
     *
     * @param path    节点路径
     * @param watcher 监听器
     * @throws CoordinatorException 协调者创建异常
     */
    EventWatching watch(
            String path,
            NodeWatcher watcher,
            boolean now
    ) throws CoordinatorException;

    /**
     * 监听给定的根节点
     * 从根节点开始（不包括根节点），所有子节点的变化都会通知。
     *
     * @param root    根节点路径
     * @param watcher 监听器
     * @throws CoordinatorException 协调者创建异常
     */
    EventWatching watch(
            String root,
            TreeWatcher watcher
    ) throws CoordinatorException;

    /**
     * 监听给定的根节点
     * 从根节点开始（不包括根节点），所有子节点的变化都会通知。
     *
     * @param root    根节点路径
     * @param watcher 监听器
     * @throws CoordinatorException 协调者创建异常
     */
    EventWatching watch(
            String root,
            TreeWatcher watcher,
            boolean now
    ) throws CoordinatorException;

    /**
     * 创建完成事件
     */
    class CreateResult {
        private final String path;
        private final String name;
        private final byte[] data;
        private final Object context;

        public CreateResult(String path, String name, byte[] data, Object context) {
            Preconditions.checkNotNull(path, "path must not be null");

            this.path = path;
            this.name = name;
            this.data = data;
            this.context = context;
        }

        public String getPath() {
            return path;
        }

        public String getName() {
            return name;
        }

        public byte[] getData() {
            return data;
        }

        public Object getContext() {
            return context;
        }
    }

    class ObtainResult {
        private final Node node;
        private final Object context;

        public ObtainResult(Node node, Object context) {
            Preconditions.checkNotNull(node, "node must not be null");

            this.node = node;
            this.context = context;
        }

        public Node getNode() {
            return node;
        }

        public Object getContext() {
            return context;
        }
    }

    class UpdateResult {
        private final String path;
        private final Object context;

        public UpdateResult(String path, Object context) {
            Preconditions.checkNotNull(path, "path must not be null");

            this.path = path;
            this.context = context;
        }

        public String getPath() {
            return path;
        }

        public Object getContext() {
            return context;
        }
    }

    class DeleteResult {
        private final String path;
        private final Object context;

        public DeleteResult(String path, Object context) {
            Preconditions.checkNotNull(path, "path must not be null");

            this.path = path;
            this.context = context;
        }

        public String getPath() {
            return path;
        }

        public Object getContext() {
            return context;
        }
    }
}
